#include "storage/DatumPage.h"
#include "contest_interface.h"
#include "access/db.h"

#include <cstdio>
#include <cstring>
#include <iostream>

#define UINT_32_SIZE 4
#define UINT_64_SIZE 8

DatumPage::DatumPage(int unit_s, BufferManager* bm, AccessType t, AttributeType *at, int dim) : Page(bm)
{
    next = NULL;
    
    unit_size = unit_s + sizeof(bool);
    
    unit_num = pageSize / unit_size;
    
    //cout << "New Page " << pageSize << " " << unit_num << endl;
    
    
    attr_num = dim;
    
	index_type = new AttributeType[attr_num];
	
    for(int i = 0; i < attr_num; i++)
	{
		index_type[i] = at[i];
    }
    
    // every access to buffer must run LoadFromFile to ensure the page is not on the disk
    LoadFromFile();
    
    char* recHead = buffer;
    
    int offset = 0;
    
    // initialize the datum page
    // set every unit as false
    // indicating these units all as available
    
    for(int i = 0; i < unit_num; i++)
    {
        bool *h = (bool*)(recHead + i * unit_size);
        
        *h = false;
    }
}

DatumPage::~DatumPage()
{
    delete [] index_type;
}

DatumPage::DatumPage(const DatumPage &dp) : Page(dp)
{
    next = dp.next;
    
    unit_size = dp.unit_size;
    
    unit_num = dp.unit_num;
    
    attr_num = dp.attr_num;
    
    index_type = new AttributeType[attr_num];
    
    for(int i = 0; i < attr_num; i++)
        index_type[i] = dp.index_type[i];
}

bool DatumPage::InsertRecord(Record* rec)
{
    char* recHead;
    int offset = 0;
    
    LoadFromFile();
    
    for(int i = 0; i < unit_num; i++)
    {
        recHead = buffer + i * unit_size;
         
        bool *h = (bool*)recHead;

        // if h is false, meaning this unit is currently available
        // write the record into the unit        
        if(!(*h))
        {
            
            offset = sizeof(bool);

            int payload_size = strlen(((char*)rec) + unit_size - MAX_PAYLOAD_LENGTH - 1);
            
            int whole_rec_size = unit_size - MAX_PAYLOAD_LENGTH + payload_size;
            
            memcpy(recHead + offset, (char*)rec, whole_rec_size);                   
            
            *h = true;
            
            return true;
        }    
    }
    
    return false;
}

void DatumPage::PrintAllRecords()
{
    //cout << "Print records in " << this << endl;
    char* recHead;
    int offset = 0;
    
    LoadFromFile();
    
    for(int i = 0; i < unit_num; i++)
    {
        recHead = buffer + i * unit_size;
         
        bool *h = (bool*)recHead;

        if(*h)
        {
            offset = sizeof(bool);

            cout << " ";
            
            for(int i = 0; i < attr_num; i++)
            {
        		if(index_type[i] == kShort)
                {
                    cout << "[" << *((int32_t*)(recHead + offset)) << "] ";
                    offset += UINT_32_SIZE;
                }
                else if(index_type[i] == kInt)
                {
                    cout << "[" << *((int64_t*)(recHead + offset)) << "] ";                    
                    offset += UINT_64_SIZE;
                }
                else if(index_type[i] == kVarchar)
                {
                    cout << "[" << recHead + offset << "] ";
                    offset += MAX_VARCHAR_LENGTH;
                }
            }
            
            // print pay_load
            cout << endl;
            
            cout << " PAY_LOAD : " << recHead + offset << endl;
        }    
    }

}
void DatumPage::CopyDatumPage(DatumPage* dest, DatumPage* source)
{
    // if the source page doesn't exist, it means the transaction intends to delete this datum page
    if(!source)
    {
        delete dest;
        
        return;
    }
    
    dest->next = source->next;
    
    dest->LoadFromFile();
    source->LoadFromFile();
    
    //cout << "swaping " << dest->next << " " << source->next << endl;
    char* tmp_pt = dest->buffer;
    dest->buffer = source->buffer;
    source->buffer = tmp_pt;
    
    //dest->bm->UpdateEntry(dest->buffer, dest->nodeID);
    //source->bm->UpdateEntry(source->buffer, source->nodeID);
    
}

